﻿using Microsoft.EntityFrameworkCore.Design;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Npgsql.EntityFrameworkCore.PostgreSQL.Infrastructure;
using Quick.EntityFrameworkCore.Extensions;
using Quick.EntityFrameworkCore.Operations;
using Quick.EntityFrameworkCore.Options;
using SampleModels;

namespace Operations.Tests
{
    [TestClass]
    public sealed class Test1
    {
        static string[] args = ["--environment", "Development"];
        private IHost _app { get; set; } = default!;
        private IServiceCollection _services { get; set; } = default!;
        private IServiceProvider _serviceProvider { get; set; } = default!;
        private IServiceScope _scope { get; set; } = default!;
        private IDesignTimeDbContextFactory<DataDbContext> designTimeFactory { get; set; } = default!;

        [TestInitialize]
        public void Init()
        {
            designTimeFactory = new DesignTimeFactory();

            var host = Host.CreateDefaultBuilder(args);

            host.ConfigureAppConfiguration((ctx, cfg) =>
            {
                cfg.AddJsonFile("appsettings.json", optional: true);
                cfg.AddJsonFile("appsettings.Development.json", optional: true);
            });

            host.ConfigureServices(configure);

            host.ConfigureServices(services => _services = services);

            _app = host.Build();
            _scope = _app.Services.CreateScope();
            _serviceProvider = _scope.ServiceProvider;
        }

        static Action<IServiceCollection> configure = (_services) =>
        {
            _services.AddLogging(o => o.ClearProviders().AddSimpleConsole());
            AutoConfiguration.Configure(_services);
            _services.AddDbContext<DataDbContext>(
                (sp, options) =>
                {
                    sp.AutoConfigure<DataDbContext>(options);
                });
            _services.AddOperations();

            _services.AddDatabaseProviderOptions<NpgsqlDbContextOptionsBuilder>(options =>
            {
                options.MigrationsAssembly("SampleModels");
            });

            _services.AddDatabaseProviderOptions<SqliteDbContextOptionsBuilder>(options =>
            {
                options.MigrationsAssembly("SampleModels");
            });
        };

        [TestMethod]
        public void Applied_NoOne()
        {
            var factory = _serviceProvider.GetRequiredService<IOperationsFactory>();
            var operations = factory.CreateMigrationOperations<DataDbContext>(args);
            var migrations = operations.GetMigrations().ToList();

            Assert.IsNotNull(migrations);
            Assert.AreEqual(2, migrations.Count);
            Assert.IsTrue(migrations.All(x => x.Applied != true));
        }

        [TestMethod]
        public void Applied_All()
        {
            var factory = _serviceProvider.GetRequiredService<IOperationsFactory>();
            var operations = factory.CreateMigrationOperations<DataDbContext>(args);
            var migrations = operations.GetMigrations(noConnect: false).ToList();

            Assert.IsNotNull(migrations);
            Assert.AreEqual(2, migrations.Count);
            Assert.IsTrue(migrations.All(x => x.Applied == true));
        }

        [TestMethod]
        public void InitDatabase()
        {
            var factory = _serviceProvider.GetRequiredService<IOperationsFactory>();
            var operations = factory.CreateMigrationOperations<DataDbContext>(args);
            var script = operations.ScriptMigrations(null, null, Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerationOptions.Script);

            Assert.IsNotNull(script);

            Console.WriteLine(script);
        }

        [TestMethod]
        public void UpVersion()
        {
            var factory = _serviceProvider.GetRequiredService<IOperationsFactory>();
            var operations = factory.CreateMigrationOperations<DataDbContext>(args);
            var script = operations.ScriptMigrations("20250116195550_Init", "20250116202544_Add_Work", Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerationOptions.Script);

            Assert.IsNotNull(script);

            Console.WriteLine(script);
        }

        [TestMethod]
        public void DownVersion()
        {
            var factory = _serviceProvider.GetRequiredService<IOperationsFactory>();
            var operations = factory.CreateMigrationOperations<DataDbContext>(args);
            var script = operations.ScriptMigrations("20250116202544_Add_Work", "20250116195550_Init", Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerationOptions.Script);

            Assert.IsNotNull(script);

            Console.WriteLine(script);
        }

        [TestMethod]
        public void DropTables()
        {
            var factory = _serviceProvider.GetRequiredService<IOperationsFactory>();
            var operations = factory.CreateMigrationOperations<DataDbContext>(args);
            var script = operations.ScriptMigrations("20250116202544_Add_Work", Migration.InitialDatabase, Microsoft.EntityFrameworkCore.Migrations.MigrationsSqlGenerationOptions.Script);

            Assert.IsNotNull(script);

            Console.WriteLine(script);
        }

        [TestMethod]
        public void UseConnectionString_NoConnect()
        {
            var factory = _serviceProvider.GetRequiredService<IOperationsFactory>();
            var operations = factory.CreateMigrationOperations<DataDbContext>(args);
            var migrations = operations.GetMigrations(connectionString: "Data Source=sample.db", noConnect: false).ToList();

            Assert.IsNotNull(migrations);
            Assert.AreEqual(2, migrations.Count);
            Assert.IsTrue(migrations.All(x => x.Applied != true));
        }


        [TestMethod]
        public void UseNpgSql_Failed()
        {
            var _cloned = _services;

            _cloned.Replace(new ServiceDescriptor(typeof(IProviderNameAccessor), typeof(AutoConfiguration.NpgsqlAccessor), ServiceLifetime.Transient));

            var _clonedServiceProvider = _cloned.BuildServiceProvider();

            var factory = _clonedServiceProvider.GetRequiredService<IOperationsFactory>();
            var operations = factory.CreateMigrationOperations<DataDbContext>(args);
            var migrations = operations.GetMigrations(connectionString: "Data Source=sample.db", noConnect: false).ToList();

            Assert.IsNotNull(migrations);
            Assert.AreEqual(2, migrations.Count);
            Assert.IsTrue(migrations.All(x => x.Applied != true));
        }
    }
}
